#include "Window.h"


//Initialize the unique_ptr's deleters here
std::unique_ptr<SDL_Window, void (*)(SDL_Window*)> Window::mWindow 
	= std::unique_ptr<SDL_Window, void (*)(SDL_Window*)>(nullptr, SDL_DestroyWindow);
std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer*)> Window::mRenderer
	= std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer*)>(nullptr, SDL_DestroyRenderer);
//Other static members
SDL_Rect Window::mBox;

void Window::Init(std::string title){
    //initialize all SDL subsystems
    if (SDL_Init(SDL_INIT_EVERYTHING|SDL_INIT_JOYSTICK) == -1)
        throw std::runtime_error("SDL Init Failed");
	if (IMG_Init(IMG_INIT_PNG) == -1)
		throw std::runtime_error("IMG Init Failed");
    if (TTF_Init() == -1)
		throw std::runtime_error("TTF Init Failed");
 
    //Setup our window size
    mBox.x = 0;
    mBox.y = 0;
	mBox.w = SCREEN_WIDTH;
	mBox.h = SCREEN_HEIGHT;
    //Create our window
    mWindow.reset(SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, 
        SDL_WINDOWPOS_CENTERED, mBox.w, mBox.h, SDL_WINDOW_SHOWN));
    //Make sure it created ok
    if (mWindow == nullptr)
        throw std::runtime_error("Failed to create window");
 
    //Create the renderer
    mRenderer.reset(SDL_CreateRenderer(mWindow.get(), -1, SDL_RENDERER_SOFTWARE));
    //Make sure it created ok
    if (mRenderer == nullptr)
        throw std::runtime_error("Failed to create renderer");
}

void Window::Init(int windowwidth,int windowheight,std::string title){
    //initialize all SDL subsystems
    if (SDL_Init(SDL_INIT_EVERYTHING) == -1)
        throw std::runtime_error("SDL Init Failed");
	if (IMG_Init(IMG_INIT_PNG) == -1)
		throw std::runtime_error("IMG Init Failed");
    if (TTF_Init() == -1)
		throw std::runtime_error("TTF Init Failed");
 
    //Setup our window size
    mBox.x = 0;
    mBox.y = 0;
    mBox.w = windowwidth;
	mBox.h = windowheight;
    //Create our window
    mWindow.reset(SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, 
        SDL_WINDOWPOS_CENTERED, mBox.w, mBox.h, SDL_WINDOW_SHOWN));
    //Make sure it created ok
    if (mWindow == nullptr)
        throw std::runtime_error("Failed to create window");
 
    //Create the renderer
    mRenderer.reset(SDL_CreateRenderer(mWindow.get(), -1, SDL_RENDERER_SOFTWARE));
    //Make sure it created ok
    if (mRenderer == nullptr)
        throw std::runtime_error("Failed to create renderer");
}

void Window::Quit(){
    TTF_Quit();
	IMG_Quit();
    SDL_Quit();
}

void Window::Draw(SDL_Texture *tex, SDL_Rect &dstRect, SDL_Rect *clip, float angle, int xPivot, int yPivot, SDL_RendererFlip flip)
{
    //Convert pivot pos from relative to object's center to screen space
    xPivot += dstRect.w / 2;
    yPivot += dstRect.h / 2;
    //SDL expects an SDL_Point as the pivot location
    SDL_Point pivot = { xPivot, yPivot };
    //Draw the texture
    SDL_RenderCopyEx(mRenderer.get(), tex, clip, &dstRect, angle, &pivot, flip);
}

SDL_Texture* Window::LoadImage(const std::string &file){
    SDL_Texture* tex = nullptr;
    tex = IMG_LoadTexture(mRenderer.get(),file.c_str());
    if (tex == nullptr)
        throw std::runtime_error("Failed to load image: " + file + IMG_GetError());
    return tex;
}

SDL_Texture* Window::RenderText(const std::string &message, const std::string &fontFile, SDL_Color color, int fontSize)
{
    //Open the font
    TTF_Font *font = nullptr;
    font = TTF_OpenFont(fontFile.c_str(), fontSize);
    if (font == nullptr)
        throw std::runtime_error("Failed to load font: " + fontFile + TTF_GetError());
	
    //Render the message to an SDL_Surface, as that's what TTF_RenderText_X returns
    SDL_Surface *surf = TTF_RenderText_Blended(font, message.c_str(), color);
	if (surf == nullptr)
        throw std::runtime_error("Failed to create surface from message: " + message + TTF_GetError());
    SDL_Texture *texture = SDL_CreateTextureFromSurface(mRenderer.get(), surf); //This line starts causing texture to be a nullptr after about 20 seconds, hmm
	if (texture == nullptr)
        throw std::runtime_error(SDL_GetError());
    //Clean up unneeded stuff
    SDL_FreeSurface(surf);
    TTF_CloseFont(font);
 
    return texture;
}

SDL_Color Window::SDL_GetColor(int r, int g, int b, int a){
	SDL_Color color;
	color.r = r;
	color.g = g;
	color.b = b;
	color.a = a;
	return color;
}

void Window::Clear(SDL_Color color)
{
	SDL_SetRenderDrawColor(mRenderer.get(), color.r, color.g, color.b, color.a);
    SDL_RenderClear(mRenderer.get());
}

void Window::Present()
{
    SDL_RenderPresent(mRenderer.get());
}

SDL_Rect Window::Box()
{
    //Update mBox to match the current window size
    SDL_GetWindowSize(mWindow.get(), &mBox.w, &mBox.h);
    return mBox;
}

SDL_Rect Window::SetBox(int x, int y)
{
	SDL_Rect box;
	box.w = DEFAULT_GRID_SIZE[0];
	box.h = DEFAULT_GRID_SIZE[1];
	box.x = x;
	box.y = y;
	return box;
}

SDL_Rect Window::SetBox(int x, int y, int w, int h)
{
	SDL_Rect box;
	box.w = w;
	box.h = h;
	box.x = x;
	box.y = y;
	return box;
}

SDL_Rect Window::GridBox(int x, int y)
{
	SDL_Rect box;
	box.w = DEFAULT_GRID_SIZE[0];
	box.h = DEFAULT_GRID_SIZE[1];
	box.x = x*DEFAULT_GRID_SIZE[0];
	box.y = y*DEFAULT_GRID_SIZE[1];
	return box;
}

SDL_Rect Window::GridLine(int y)
{
	SDL_Rect box = Window::Box();
	box.h = DEFAULT_GRID_SIZE[1];
	box.y = y*DEFAULT_GRID_SIZE[1];
	return box;
}

void Window::DrawTiles(vector<string> image)
{
	for(size_t i = 0; i<image.size();i++)
	{
		SDL_Texture* texture = Window::RenderText(image[i],"VeraMono.ttf",DEFAULT_SDL_TEXT_COLOR,24);
		Window::Draw(texture, Window::GridLine(i));
		SDL_DestroyTexture(texture);
	}
	Window::Present();
}

void Window::DrawTiles(vector<string> image, const std::string& fontFile, SDL_Color textColor, int fontSize)
{
	for(size_t i = 0; i<image.size();i++)
	{
		Window::Draw(Window::RenderText(image[i],fontFile,textColor,fontSize),Window::GridLine(i));
	}
	Window::Present();
}

void Window::DrawDot(int x, int y, int radius,SDL_Color color)
{
	SDL_SetRenderDrawColor(mRenderer.get(), color.r, color.g, color.b, color.a);
	SDL_RenderDrawPoint(mRenderer.get(),x,y);
}

void Window::DrawLine(int x1,int y1, int x2, int y2,SDL_Color color)
{
	SDL_SetRenderDrawColor(mRenderer.get(), color.r, color.g, color.b, color.a);
	SDL_RenderDrawLine(mRenderer.get(),(Sint16) x1,(Sint16) y1,(Sint16) x2,(Sint16) y2);
}

void Window::DrawRegularPolygon(unsigned int sides,SDL_Color color) //Circumscribed in the drawZone. Angle is counted clockwise from the vertical in radians.
{
	int centerX = SCREEN_WIDTH/2;
	int centerY = SCREEN_HEIGHT/2;
	for(unsigned int i=0; i<sides; i++)
	{
		DrawLine(centerX+static_cast<int>(centerX*drawZone*cos((2*M_PI*i)/sides)+0.5),centerY-static_cast<int>(centerY*drawZone*sin((2*M_PI*i)/sides)+0.5),centerX+static_cast<int>(centerX*drawZone*cos((2*M_PI*(i+1))/sides)+0.5),centerY-static_cast<int>(centerY*drawZone*sin((2*M_PI*(i+1))/sides)+0.5), color);
	}
}